from typing import Dict, Any, List, Optional
from datetime import datetime
from bson import ObjectId
from pymongo.collection import Collection
from utils.logger import get_logger
from utils.exceptions import (
    ContractNotFoundException,
    VersionNotFoundException,
    ValidationException,
    VersionConflictException
)

logger = get_logger(__name__)

class SmartContractVersionManager:
    """智能合约版本管理服务 (SCVM)
    
    负责:
    1. 合约版本控制和管理
    2. 合约升级策略实施
    3. 版本回滚机制
    4. 灰度发布管理
    5. 跨链版本同步
    """
    
    def __init__(self, mongo_db):
        self.db = mongo_db
        self.versions_collection: Collection = mongo_db.contract_versions
        self.upgrades_collection: Collection = mongo_db.contract_upgrades
        self.rollbacks_collection: Collection = mongo_db.contract_rollbacks
        self.deployments_collection: Collection = mongo_db.version_deployments
        
        # 创建索引
        self._create_indexes()
    
    def _create_indexes(self):
        """创建数据库索引"""
        try:
            # 版本集合索引
            self.versions_collection.create_index([("contract_id", 1), ("version", 1)], unique=True)
            self.versions_collection.create_index("contract_id")
            self.versions_collection.create_index("version")
            self.versions_collection.create_index("status")
            self.versions_collection.create_index("created_at")
            
            # 升级记录集合索引
            self.upgrades_collection.create_index("contract_id")
            self.upgrades_collection.create_index("from_version")
            self.upgrades_collection.create_index("to_version")
            self.upgrades_collection.create_index("upgrade_type")
            self.upgrades_collection.create_index("created_at")
            
            # 回滚记录集合索引
            self.rollbacks_collection.create_index("contract_id")
            self.rollbacks_collection.create_index("from_version")
            self.rollbacks_collection.create_index("to_version")
            self.rollbacks_collection.create_index("created_at")
            
            # 部署记录集合索引
            self.deployments_collection.create_index("version_id")
            self.deployments_collection.create_index("chain_id")
            self.deployments_collection.create_index("deployment_type")
            self.deployments_collection.create_index("created_at")
            
        except Exception as e:
            logger.warning(f"Failed to create indexes: {str(e)}")
    
    def create_version(self, version_data: Dict[str, Any]) -> str:
        """创建新版本"""
        try:
            # 验证必需字段
            required_fields = ['contract_id', 'version', 'source_code', 'abi']
            for field in required_fields:
                if field not in version_data:
                    raise ValidationException(f"Missing required field: {field}")
            
            # 检查版本是否已存在
            existing = self.versions_collection.find_one({
                'contract_id': version_data['contract_id'],
                'version': version_data['version']
            })
            
            if existing:
                raise VersionConflictException(
                    f"Version {version_data['version']} already exists for contract {version_data['contract_id']}"
                )
            
            # 准备版本文档
            version_doc = {
                'contract_id': version_data['contract_id'],
                'version': version_data['version'],
                'source_code': version_data['source_code'],
                'abi': version_data['abi'],
                'bytecode': version_data.get('bytecode'),
                'compiler_version': version_data.get('compiler_version'),
                'description': version_data.get('description'),
                'changelog': version_data.get('changelog'),
                'breaking_changes': version_data.get('breaking_changes', False),
                'upgrade_strategy': version_data.get('upgrade_strategy', 'direct'),  # direct, proxy, migration
                'migration_script': version_data.get('migration_script'),
                'status': 'draft',  # draft, testing, staging, production, deprecated
                'created_by': version_data.get('created_by'),
                'created_at': datetime.utcnow(),
                'updated_at': datetime.utcnow(),
                'metadata': version_data.get('metadata', {})
            }
            
            # 插入版本记录
            result = self.versions_collection.insert_one(version_doc)
            version_id = str(result.inserted_id)
            
            logger.info(f"Contract version created: {version_id}")
            return version_id
            
        except Exception as e:
            logger.error(f"Failed to create contract version: {str(e)}")
            if isinstance(e, (ValidationException, VersionConflictException)):
                raise
            raise ValidationException(f"Version creation failed: {str(e)}")
    
    def get_version(self, version_id: str) -> Dict[str, Any]:
        """获取版本信息"""
        try:
            version = self.versions_collection.find_one({'_id': ObjectId(version_id)})
            if not version:
                raise VersionNotFoundException("Version not found")
            
            version['_id'] = str(version['_id'])
            return version
            
        except Exception as e:
            if isinstance(e, VersionNotFoundException):
                raise
            logger.error(f"Failed to get version: {str(e)}")
            raise VersionNotFoundException(f"Failed to retrieve version: {str(e)}")
    
    def get_contract_versions(self, contract_id: str, status: str = None) -> List[Dict[str, Any]]:
        """获取合约的所有版本"""
        try:
            query = {'contract_id': contract_id}
            if status:
                query['status'] = status
            
            cursor = self.versions_collection.find(query).sort('created_at', -1)
            versions = list(cursor)
            
            for version in versions:
                version['_id'] = str(version['_id'])
            
            return versions
            
        except Exception as e:
            logger.error(f"Failed to get contract versions: {str(e)}")
            raise
    
    def get_latest_version(self, contract_id: str, status: str = 'production') -> Dict[str, Any]:
        """获取最新版本"""
        try:
            version = self.versions_collection.find_one(
                {'contract_id': contract_id, 'status': status},
                sort=[('created_at', -1)]
            )
            
            if not version:
                raise VersionNotFoundException(f"No {status} version found for contract {contract_id}")
            
            version['_id'] = str(version['_id'])
            return version
            
        except Exception as e:
            if isinstance(e, VersionNotFoundException):
                raise
            logger.error(f"Failed to get latest version: {str(e)}")
            raise
    
    def update_version_status(self, version_id: str, status: str, notes: str = None) -> bool:
        """更新版本状态"""
        try:
            valid_statuses = ['draft', 'testing', 'staging', 'production', 'deprecated']
            if status not in valid_statuses:
                raise ValidationException(f"Invalid status. Must be one of: {valid_statuses}")
            
            update_data = {
                'status': status,
                'updated_at': datetime.utcnow()
            }
            
            if notes:
                update_data['status_notes'] = notes
            
            # 如果设置为生产状态，需要将其他生产版本设为deprecated
            if status == 'production':
                version = self.get_version(version_id)
                self.versions_collection.update_many(
                    {
                        'contract_id': version['contract_id'],
                        'status': 'production',
                        '_id': {'$ne': ObjectId(version_id)}
                    },
                    {'$set': {'status': 'deprecated', 'updated_at': datetime.utcnow()}}
                )
            
            result = self.versions_collection.update_one(
                {'_id': ObjectId(version_id)},
                {'$set': update_data}
            )
            
            if result.matched_count == 0:
                raise VersionNotFoundException("Version not found")
            
            logger.info(f"Version status updated: {version_id} -> {status}")
            return True
            
        except Exception as e:
            if isinstance(e, (VersionNotFoundException, ValidationException)):
                raise
            logger.error(f"Failed to update version status: {str(e)}")
            raise
    
    def create_upgrade_plan(self, upgrade_data: Dict[str, Any]) -> str:
        """创建升级计划"""
        try:
            # 验证必需字段
            required_fields = ['contract_id', 'from_version', 'to_version', 'upgrade_type']
            for field in required_fields:
                if field not in upgrade_data:
                    raise ValidationException(f"Missing required field: {field}")
            
            # 验证升级类型
            valid_types = ['direct', 'proxy', 'migration', 'canary']
            if upgrade_data['upgrade_type'] not in valid_types:
                raise ValidationException(f"Invalid upgrade type. Must be one of: {valid_types}")
            
            # 准备升级计划文档
            upgrade_doc = {
                'contract_id': upgrade_data['contract_id'],
                'from_version': upgrade_data['from_version'],
                'to_version': upgrade_data['to_version'],
                'upgrade_type': upgrade_data['upgrade_type'],
                'description': upgrade_data.get('description'),
                'rollback_plan': upgrade_data.get('rollback_plan'),
                'testing_plan': upgrade_data.get('testing_plan'),
                'deployment_schedule': upgrade_data.get('deployment_schedule'),
                'canary_percentage': upgrade_data.get('canary_percentage', 0),
                'approval_required': upgrade_data.get('approval_required', True),
                'approved_by': upgrade_data.get('approved_by'),
                'approved_at': upgrade_data.get('approved_at'),
                'status': 'planned',  # planned, approved, in_progress, completed, failed, cancelled
                'created_by': upgrade_data.get('created_by'),
                'created_at': datetime.utcnow(),
                'updated_at': datetime.utcnow(),
                'metadata': upgrade_data.get('metadata', {})
            }
            
            result = self.upgrades_collection.insert_one(upgrade_doc)
            upgrade_id = str(result.inserted_id)
            
            logger.info(f"Upgrade plan created: {upgrade_id}")
            return upgrade_id
            
        except Exception as e:
            logger.error(f"Failed to create upgrade plan: {str(e)}")
            if isinstance(e, ValidationException):
                raise
            raise ValidationException(f"Upgrade plan creation failed: {str(e)}")
    
    def execute_upgrade(self, upgrade_id: str, executor: str) -> Dict[str, Any]:
        """执行升级"""
        try:
            # 获取升级计划
            upgrade = self.upgrades_collection.find_one({'_id': ObjectId(upgrade_id)})
            if not upgrade:
                raise VersionNotFoundException("Upgrade plan not found")
            
            if upgrade['status'] != 'approved':
                raise ValidationException("Upgrade plan must be approved before execution")
            
            # 更新升级状态
            self.upgrades_collection.update_one(
                {'_id': ObjectId(upgrade_id)},
                {
                    '$set': {
                        'status': 'in_progress',
                        'executed_by': executor,
                        'execution_started_at': datetime.utcnow(),
                        'updated_at': datetime.utcnow()
                    }
                }
            )
            
            # 根据升级类型执行不同的升级策略
            result = self._execute_upgrade_strategy(upgrade)
            
            # 更新升级状态为完成
            self.upgrades_collection.update_one(
                {'_id': ObjectId(upgrade_id)},
                {
                    '$set': {
                        'status': 'completed',
                        'execution_completed_at': datetime.utcnow(),
                        'execution_result': result,
                        'updated_at': datetime.utcnow()
                    }
                }
            )
            
            logger.info(f"Upgrade executed successfully: {upgrade_id}")
            return result
            
        except Exception as e:
            # 更新升级状态为失败
            self.upgrades_collection.update_one(
                {'_id': ObjectId(upgrade_id)},
                {
                    '$set': {
                        'status': 'failed',
                        'execution_error': str(e),
                        'updated_at': datetime.utcnow()
                    }
                }
            )
            
            logger.error(f"Failed to execute upgrade: {str(e)}")
            if isinstance(e, (VersionNotFoundException, ValidationException)):
                raise
            raise ValidationException(f"Upgrade execution failed: {str(e)}")
    
    def _execute_upgrade_strategy(self, upgrade: Dict[str, Any]) -> Dict[str, Any]:
        """执行具体的升级策略"""
        upgrade_type = upgrade['upgrade_type']
        
        if upgrade_type == 'direct':
            return self._execute_direct_upgrade(upgrade)
        elif upgrade_type == 'proxy':
            return self._execute_proxy_upgrade(upgrade)
        elif upgrade_type == 'migration':
            return self._execute_migration_upgrade(upgrade)
        elif upgrade_type == 'canary':
            return self._execute_canary_upgrade(upgrade)
        else:
            raise ValidationException(f"Unsupported upgrade type: {upgrade_type}")
    
    def _execute_direct_upgrade(self, upgrade: Dict[str, Any]) -> Dict[str, Any]:
        """执行直接升级"""
        # 直接升级：部署新版本合约，更新引用
        return {
            'upgrade_type': 'direct',
            'message': 'Direct upgrade completed',
            'timestamp': datetime.utcnow().isoformat()
        }
    
    def _execute_proxy_upgrade(self, upgrade: Dict[str, Any]) -> Dict[str, Any]:
        """执行代理升级"""
        # 代理升级：通过代理合约升级实现逻辑
        return {
            'upgrade_type': 'proxy',
            'message': 'Proxy upgrade completed',
            'timestamp': datetime.utcnow().isoformat()
        }
    
    def _execute_migration_upgrade(self, upgrade: Dict[str, Any]) -> Dict[str, Any]:
        """执行迁移升级"""
        # 迁移升级：执行数据迁移脚本
        return {
            'upgrade_type': 'migration',
            'message': 'Migration upgrade completed',
            'timestamp': datetime.utcnow().isoformat()
        }
    
    def _execute_canary_upgrade(self, upgrade: Dict[str, Any]) -> Dict[str, Any]:
        """执行金丝雀升级"""
        # 金丝雀升级：逐步部署到部分节点
        return {
            'upgrade_type': 'canary',
            'message': 'Canary upgrade completed',
            'canary_percentage': upgrade.get('canary_percentage', 0),
            'timestamp': datetime.utcnow().isoformat()
        }
    
    def create_rollback_plan(self, rollback_data: Dict[str, Any]) -> str:
        """创建回滚计划"""
        try:
            # 验证必需字段
            required_fields = ['contract_id', 'from_version', 'to_version', 'reason']
            for field in required_fields:
                if field not in rollback_data:
                    raise ValidationException(f"Missing required field: {field}")
            
            # 准备回滚计划文档
            rollback_doc = {
                'contract_id': rollback_data['contract_id'],
                'from_version': rollback_data['from_version'],
                'to_version': rollback_data['to_version'],
                'reason': rollback_data['reason'],
                'description': rollback_data.get('description'),
                'impact_assessment': rollback_data.get('impact_assessment'),
                'data_migration_required': rollback_data.get('data_migration_required', False),
                'migration_script': rollback_data.get('migration_script'),
                'approval_required': rollback_data.get('approval_required', True),
                'approved_by': rollback_data.get('approved_by'),
                'approved_at': rollback_data.get('approved_at'),
                'status': 'planned',  # planned, approved, in_progress, completed, failed, cancelled
                'created_by': rollback_data.get('created_by'),
                'created_at': datetime.utcnow(),
                'updated_at': datetime.utcnow(),
                'metadata': rollback_data.get('metadata', {})
            }
            
            result = self.rollbacks_collection.insert_one(rollback_doc)
            rollback_id = str(result.inserted_id)
            
            logger.info(f"Rollback plan created: {rollback_id}")
            return rollback_id
            
        except Exception as e:
            logger.error(f"Failed to create rollback plan: {str(e)}")
            if isinstance(e, ValidationException):
                raise
            raise ValidationException(f"Rollback plan creation failed: {str(e)}")
    
    def execute_rollback(self, rollback_id: str, executor: str) -> Dict[str, Any]:
        """执行回滚"""
        try:
            # 获取回滚计划
            rollback = self.rollbacks_collection.find_one({'_id': ObjectId(rollback_id)})
            if not rollback:
                raise VersionNotFoundException("Rollback plan not found")
            
            if rollback['status'] != 'approved':
                raise ValidationException("Rollback plan must be approved before execution")
            
            # 更新回滚状态
            self.rollbacks_collection.update_one(
                {'_id': ObjectId(rollback_id)},
                {
                    '$set': {
                        'status': 'in_progress',
                        'executed_by': executor,
                        'execution_started_at': datetime.utcnow(),
                        'updated_at': datetime.utcnow()
                    }
                }
            )
            
            # 执行回滚操作
            result = self._execute_rollback_operation(rollback)
            
            # 更新回滚状态为完成
            self.rollbacks_collection.update_one(
                {'_id': ObjectId(rollback_id)},
                {
                    '$set': {
                        'status': 'completed',
                        'execution_completed_at': datetime.utcnow(),
                        'execution_result': result,
                        'updated_at': datetime.utcnow()
                    }
                }
            )
            
            logger.info(f"Rollback executed successfully: {rollback_id}")
            return result
            
        except Exception as e:
            # 更新回滚状态为失败
            self.rollbacks_collection.update_one(
                {'_id': ObjectId(rollback_id)},
                {
                    '$set': {
                        'status': 'failed',
                        'execution_error': str(e),
                        'updated_at': datetime.utcnow()
                    }
                }
            )
            
            logger.error(f"Failed to execute rollback: {str(e)}")
            if isinstance(e, (VersionNotFoundException, ValidationException)):
                raise
            raise ValidationException(f"Rollback execution failed: {str(e)}")
    
    def _execute_rollback_operation(self, rollback: Dict[str, Any]) -> Dict[str, Any]:
        """执行回滚操作"""
        # 实际的回滚逻辑实现
        return {
            'message': 'Rollback completed successfully',
            'from_version': rollback['from_version'],
            'to_version': rollback['to_version'],
            'timestamp': datetime.utcnow().isoformat()
        }
    
    def get_version_history(self, contract_id: str) -> List[Dict[str, Any]]:
        """获取版本历史"""
        try:
            # 获取所有版本
            versions = self.get_contract_versions(contract_id)
            
            # 获取升级记录
            upgrades = list(self.upgrades_collection.find(
                {'contract_id': contract_id}
            ).sort('created_at', 1))
            
            # 获取回滚记录
            rollbacks = list(self.rollbacks_collection.find(
                {'contract_id': contract_id}
            ).sort('created_at', 1))
            
            # 构建版本历史
            history = []
            
            for version in versions:
                history.append({
                    'type': 'version',
                    'version': version['version'],
                    'status': version['status'],
                    'created_at': version['created_at'],
                    'description': version.get('description')
                })
            
            for upgrade in upgrades:
                upgrade['_id'] = str(upgrade['_id'])
                history.append({
                    'type': 'upgrade',
                    'from_version': upgrade['from_version'],
                    'to_version': upgrade['to_version'],
                    'upgrade_type': upgrade['upgrade_type'],
                    'status': upgrade['status'],
                    'created_at': upgrade['created_at']
                })
            
            for rollback in rollbacks:
                rollback['_id'] = str(rollback['_id'])
                history.append({
                    'type': 'rollback',
                    'from_version': rollback['from_version'],
                    'to_version': rollback['to_version'],
                    'reason': rollback['reason'],
                    'status': rollback['status'],
                    'created_at': rollback['created_at']
                })
            
            # 按时间排序
            history.sort(key=lambda x: x['created_at'])
            
            return history
            
        except Exception as e:
            logger.error(f"Failed to get version history: {str(e)}")
            raise